/**
 ******************************************************************************
 * @file           : bsp_w25qxx.c
 * @brief          : W25Qxx Flash Memory Driver (STM32F4xx)
 ******************************************************************************
 */

#include "./Devices/W25Qxx/bsp_w25qxx.h"
#include "./Devices/W25Qxx/bsp_w25qxx_config.h"
#include "./BSP/SPI/bsp_spi.h"

#define W25QXX_CMD_READ_ID      0x9F
#define W25QXX_CMD_READ         0x03
#define W25QXX_CMD_WRITE        0x02
#define W25QXX_CMD_WRITE_EN     0x06
#define W25QXX_CMD_ERASE_SEC    0x20
#define W25QXX_CMD_ERASE_CHIP   0xC7

static void w25qxx_cs_low(void)
{
    HAL_GPIO_WritePin(W25QXX_CS_GPIO_PORT, W25QXX_CS_GPIO_PIN, GPIO_PIN_RESET);
}

static void w25qxx_cs_high(void)
{
    HAL_GPIO_WritePin(W25QXX_CS_GPIO_PORT, W25QXX_CS_GPIO_PIN, GPIO_PIN_SET);
}

uint8_t w25qxx_init(void)
{
    GPIO_InitTypeDef gpio_init = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    gpio_init.Pin = W25QXX_CS_GPIO_PIN;
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init.Pull = GPIO_NOPULL;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(W25QXX_CS_GPIO_PORT, &gpio_init);
    
    w25qxx_cs_high();
    
    log_i("W25Qxx initialized");
    return 0;
}

void w25qxx_deinit(void)
{
    HAL_GPIO_DeInit(W25QXX_CS_GPIO_PORT, W25QXX_CS_GPIO_PIN);
}

uint32_t w25qxx_read_id(void)
{
    uint8_t id[3];
    
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_READ_ID);
    id[0] = spi_read_byte();
    id[1] = spi_read_byte();
    id[2] = spi_read_byte();
    w25qxx_cs_high();
    
    return (id[0] << 16) | (id[1] << 8) | id[2];
}

uint8_t w25qxx_read(uint32_t addr, uint8_t *buf, uint16_t len)
{
    if (buf == NULL || len == 0)
    {
        return 1;
    }
    
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_READ);
    spi_send_byte((addr >> 16) & 0xFF);
    spi_send_byte((addr >> 8) & 0xFF);
    spi_send_byte(addr & 0xFF);
    
    for (uint16_t i = 0; i < len; i++)
    {
        buf[i] = spi_read_byte();
    }
    
    w25qxx_cs_high();
    return 0;
}

uint8_t w25qxx_write(uint32_t addr, const uint8_t *buf, uint16_t len)
{
    if (buf == NULL || len == 0)
    {
        return 1;
    }
    
    /* 写使能 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_WRITE_EN);
    w25qxx_cs_high();
    
    /* 写数据 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_WRITE);
    spi_send_byte((addr >> 16) & 0xFF);
    spi_send_byte((addr >> 8) & 0xFF);
    spi_send_byte(addr & 0xFF);
    
    for (uint16_t i = 0; i < len; i++)
    {
        spi_send_byte(buf[i]);
    }
    
    w25qxx_cs_high();
    HAL_Delay(5);
    
    return 0;
}

uint8_t w25qxx_erase_sector(uint32_t addr)
{
    /* 写使能 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_WRITE_EN);
    w25qxx_cs_high();
    
    /* 擦除扇区 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_ERASE_SEC);
    spi_send_byte((addr >> 16) & 0xFF);
    spi_send_byte((addr >> 8) & 0xFF);
    spi_send_byte(addr & 0xFF);
    w25qxx_cs_high();
    
    HAL_Delay(100);
    return 0;
}

uint8_t w25qxx_erase_chip(void)
{
    /* 写使能 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_WRITE_EN);
    w25qxx_cs_high();
    
    /* 擦除整片 */
    w25qxx_cs_low();
    spi_send_byte(W25QXX_CMD_ERASE_CHIP);
    w25qxx_cs_high();
    
    HAL_Delay(1000);
    return 0;
}
